//  Copyright Druid Mechanics


#include "AbilitySystem/Abilities/AuraProjectileSpell.h"
#include "Actor/AuraProjectile.h"
#include "Interaction/CombatInterface.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "AbilitySystemComponent.h"
#include "AuraGameplayTags.h"
#include "Kismet/KismetSystemLibrary.h"

void UAuraProjectileSpell::ActivateAbility(const FGameplayAbilitySpecHandle Handle,
                                           const FGameplayAbilityActorInfo* ActorInfo,
                                           const FGameplayAbilityActivationInfo ActivationInfo,
                                           const FGameplayEventData* TriggerEventData)
{
	Super::ActivateAbility(Handle, ActorInfo, ActivationInfo, TriggerEventData);
}

void UAuraProjectileSpell::SpawnProjectile(const FVector& ProjectileTargetLocation, const FGameplayTag& SocketTag,
                                           bool bOverridePath, float PitchOverride)
{
	/**
	 * HasAuthority: 若为true，则GA在服务端或独立玩家。
	 */
	const bool bIsServer = GetAvatarActorFromActorInfo()->HasAuthority();
	if (!bIsServer)
	{
		return;
	}
	/**
	 * GetAvatarActorFromActorInfo: 返回触发此能力的物理Actor。可能为空。
	 */
	FVector SocketLocation =
		ICombatInterface::Execute_GetCombatSocketLocation(GetAvatarActorFromActorInfo(), SocketTag);
	FVector DeltaDirection = ProjectileTargetLocation - SocketLocation;
	FRotator Rotation = (DeltaDirection).Rotation();
	if (bOverridePath)
	{
		Rotation.Pitch = PitchOverride;
	}
	/**
	 * IsDedicatedServer: 返回是否运行在专用服务器。
	 */
	if (UKismetSystemLibrary::IsDedicatedServer(GetWorld()))
	{
		/**
		 * 解决专用服务器客户端的发射炮弹问题。
		 */
		SocketLocation = SocketLocation + DeltaDirection.GetSafeNormal() * 60.f;
	}
	FTransform SpawnTransform;
	SpawnTransform.SetLocation(SocketLocation);
	SpawnTransform.SetRotation(Rotation.Quaternion());
	/**
	 * SpawnActorDeferred<T>:
	 * 孵化给定类并返回类型指针，强制设置世界转换。
	 * 让Caller有机会预先设置参数。Caller负责调用构造手动调用UGameplayStatics::FinishSpawningActor，或调用Projectile->FinishSpawning(SpawnTransform)函数。
	 */
	/**
	 * GetOwningActorFromActorInfo: 返回这个能力的Owning Actor，此Actor可能没有物理位置。
	 * virtual void InitAbilityActorInfo(AActor* InOwnerActor, AActor* InAvatarActor);与此函数传参相互对应。
	 */
	AAuraProjectile* Projectile = GetWorld()->SpawnActorDeferred<AAuraProjectile>(
		ProjectileClass, SpawnTransform, GetOwningActorFromActorInfo(),
		Cast<APawn>(GetOwningActorFromActorInfo()),
		ESpawnActorCollisionHandlingMethod::AlwaysSpawn);
#pragma region 视频教育方法
	//
	// const UAbilitySystemComponent* SourceASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(
	// 	GetAvatarActorFromActorInfo());
	//
	// FGameplayEffectContextHandle EffectContextHandle = SourceASC->MakeEffectContext();
	// /**
	//  * SetAbility: 在上下文中设置GA实例和CDO参数。
	//  */
	// EffectContextHandle.SetAbility(this);
	// EffectContextHandle.AddSourceObject(Projectile);
	// TArray<TWeakObjectPtr<AActor>> Actors;
	// Actors.Add(Projectile);
	// /**
	//  * AddActors: 添加Actor到Actor存储列表。
	//  */
	// EffectContextHandle.AddActors(Actors);
	//
	// FHitResult HitResult;
	// /**
	//  * AddHitResult: 添加一个用于Target定位的命中结果。
	//  */
	// HitResult.Location = ProjectileTargetLocation;
	// EffectContextHandle.AddHitResult(HitResult);
	//
	// const FGameplayEffectSpecHandle SpecHandle = SourceASC->MakeOutgoingSpec(
	// 	DamageEffectClass, GetAbilityLevel(), EffectContextHandle);
	//
	// const FAuraGameplayTags GameplayTags = FAuraGameplayTags::Get();
	// /**
	//  * GetValueAtLevel: 给定等级获取缩放值。
	//  */
	// const float ScaledDamage = Damage.GetValueAtLevel(GetAbilityLevel());
	// /**
	//  * AssignSetByCallerMagnitude: 设置一个由调用者设定的GT(GameplayTag)的数值。 
	//  */
	// UAbilitySystemBlueprintLibrary::AssignTagSetByCallerMagnitude(SpecHandle, DamageType, ScaledDamage);
	//
	// /**
	//  * Damage.AsInteger()
	//  * AsInteger: 将该值作为32位整数返回。
	//  */
	// /**
	//  * Damage.EvaluateCurveAtLevel();
	//  * EvaluateCurveAtLevel: 在给定等级获取值以及可能存在的曲线，如果获取好的值失败则返回false。
	//  */
	//
	// Projectile->DamageEffectSpecHandle = SpecHandle;
	/**
	 * FinishSpawning: 通常在延期生成的情况下，调用完成孵化进程。
	 */
#pragma endregion
	Projectile->DamageEffectParams = MakeDamageEffectParamsFromClassDefaults();
	Projectile->FinishSpawning(SpawnTransform);
}
